Show / Hide Table of Contents

一般属性用于存储实体中的一个值数据。

示例

以下代码为 Warehouse 类声明了一个名称为 Name 的一般属性。

public static readonly Property<string> NameProperty = P<Warehouse>.Register(e => e.Name);
public string Name
{
    get { return this.GetProperty(NameProperty); }
    set { this.SetProperty(NameProperty, value); }
}

一般属性包含一个类型为 Property 的属性标识,以及一个对应的 CLR 属性包装。

支持的属性类型

一般属性可以声明为任何类型。但是只支持以下类型属性类型(如果该类型是值类型,则包含相应的可空类型。)到数据库的映射:

  • string
  • bool、bool?
  • int、int?, long, long?
  • double、double?
  • DateTime、DateTime?
  • 枚举类型、Nullable<枚举类型>
  • Byte[]

属性逻辑扩展

一般属性的逻辑的扩展方式有以下三种:属性设置前回调、属性设置后回调、更换属性获取逻辑。这三种扩展都是通过在定义属性的同时指定该属性的元数据中的回调委托来完成的。

  • 编写属性变更前逻辑
    以下代码为 Warehouse(仓库)实体的 Name 属性添加属性变更前事件委托:

    public static readonly Property<string> NameProperty = P<Warehouse>.Register(e => e.Name, new PropertyMetadata<string>
    {
      PropertyChangingCallBack = (o, e) => (o as Warehouse).OnNameChanging(e)
    });
    public string Name
    {
      get { return this.GetProperty(NameProperty); }
      set { this.SetProperty(NameProperty, value); }
    }
    protected virtual void OnNameChanging(ManagedPropertyChangingEventArgs<string> e)
    {
      if (string.IsNullOrWhiteSpace(e.Value))
      {
          e.Cancel = true;
      }
      if (e.Value.Length > 10)
      {
          e.CoercedValue = e.Value.Substring(0, 10);
      }
    }
    

通过设置 PropertyMetadata.PropertyChangingCallBack 委托,来调用实体的实例方法 OnNameChanging,完成了两个功能:如果设置的值是空字符串,则忽略本次设置;如果设置的字符串长度大于 10,则只取字符串的前 10 个字符作为仓库的名称。同时,通过把 OnNameChanging 设计为实例虚方法,可以支持继承此类的子类方便地重写此处的逻辑。

  • 编写属性变更后逻辑
    以下代码为 Warehouse(仓库)实体的 Name 属性添加属性变更后事件委托:

    public static readonly Property<string> NameProperty = P<Warehouse>.Register(e => e.Name, new PropertyMetadata<string>
    {
     PropertyChangedCallBack = (o, e) => (o as Warehouse).OnNameChanged(e)
    });
    public string Name
    {
     get { return this.GetProperty(NameProperty); }
     set { this.SetProperty(NameProperty, value); }
    }
    protected virtual void OnNameChanged(ManagedPropertyChangedEventArgs e)
    {
     string oldName = e.OldValue as string;
     string newName = e.NewValue as string;
     LogNameChanged(oldName, newName);
    }
    

通过设置 PropertyMetadata.PropertyChangedCallBack 委托,来调用实体的实例方法 OnNameChanged 方法。OnNameChanged 可以获取到属性变更前后的两个值,并记录到名称变更日志中。
由于属性变更后事件使用场景较多,实体框架还提供了更简单的一种处理方法:实体的属性值在变更时,都会调用实体的OnPropertyChanged方法。Entity.OnPropertyChanged(ManagedPropertyChangedEventArgs e)虚方法,完成与上段代码相同的逻辑:

protected override void OnPropertyChanged(ManagedPropertyChangedEventArgs e)
{
   if (e.Property == NameProperty)
   {
       string oldName = e.OldValue as string;
       string newName = e.NewValue as string;
       LogNameChanged(oldName, newName);
   }

   base.OnPropertyChanged(e);
}
  • 更换属性值获取逻辑
    一般情况下,只需要编写属性变更前逻辑就可以完全控制属性存储的值了。但是框架还额外提供了属性的获取逻辑委托,使得开发人员可以重写属性获取逻辑。(尽量使用属性变更前逻辑。)
    以下代码为 Warehouse(仓库)实体的 Name 属性更换属性获取强制逻辑:

    public static readonly Property<string> NameProperty = P<Warehouse>.Register(e => e.Name, new PropertyMetadata<string>
    {
     CoerceGetValueCallBack = (o, v) => (o as Warehouse).CoerceGetName(v)
    });
    public string Name
    {
     get { return this.GetProperty(NameProperty); }
     set { this.SetProperty(NameProperty, value); }
    }
    protected virtual string CoerceGetName(string value)
    {
     if (value.Length > 10)
     {
         return value.Substring(0, 10);
     }
     return value;
    }
    

通过设置 PropertyMetadata.CoerceGetValueCallBack 委托,调用实体的实例方法 CoerceGetName,完成了与属性变更前事件相同的逻辑:如果名称的长度大于 10,则只返回前 10 个字符作为仓库的名称。而与重写设置前事件不同的地方在于,这个属性内部存储的值实际上长度是超过 10 的,只是通过 GetProperty 只能获取前 10 个字符而已。例如,如果设置了这个属性值为 "123456789012345",在存储数据库中后,它的长度就是这个字符串,但是通过属性的获取方法,返回的是 "1234567890"。

  • Improve this Doc
In This Article
Back to top 有问题请联系 9474649@qq.com。